#include "Opcode.h"
#include <stdarg.h>
#include "swf/SWFInputStream.h"
#include "ABCFile.h"

#define MAKE_OP(field, count, ...) \
	OpcodeOperation* OpcodeOperation::op_##field = new OpcodeOperation(Opcode::OP_##field, #field, count, __VA_ARGS__)

OpcodeOperation::OpcodeOperation(Opcode code, std::string name, int count, ...)
{
	this->code = code;
	this->name = name;
	opMap[code] = this;
	va_list list;
	va_start(list, count);
	while (count > 0)
	{
		count--;
		OpcodeArgType t = va_arg(list, OpcodeArgType);
		this->arguments.push_back(t);
	}
	va_end(list);
}

OpcodeOperation* OpcodeOperation::getOperation( Opcode code )
{
	return opMap[code];
}

std::map<Opcode, OpcodeOperation*> OpcodeOperation::opMap;

MAKE_OP(add, 0);
MAKE_OP(add_i, 0);
MAKE_OP(astype, 1, OpcodeArgType::A_Multiname);
MAKE_OP(astypelate, 0);
MAKE_OP(applytype, 1, OpcodeArgType::A_U30);
MAKE_OP(bitand, 0);
MAKE_OP(bitnot, 0);
MAKE_OP(bitor, 0);
MAKE_OP(bitxor, 0);
MAKE_OP(call, 1, OpcodeArgType::A_U30);
MAKE_OP(callmethod, 2, OpcodeArgType::A_U30, OpcodeArgType::A_U30);
MAKE_OP(callproperty, 2, OpcodeArgType::A_Multiname, OpcodeArgType::A_U30);
MAKE_OP(callproplex, 2, OpcodeArgType::A_Multiname, OpcodeArgType::A_U30);
MAKE_OP(callpropvoid, 2, OpcodeArgType::A_Multiname, OpcodeArgType::A_U30);
MAKE_OP(callstatic, 2, OpcodeArgType::A_U30, OpcodeArgType::A_U30);//
MAKE_OP(callsuper, 2, OpcodeArgType::A_Multiname, OpcodeArgType::A_U30);
MAKE_OP(callsupervoid, 2, OpcodeArgType::A_Multiname, OpcodeArgType::A_U30);
MAKE_OP(checkfilter, 0);
MAKE_OP(coerce, 1, OpcodeArgType::A_Multiname);
MAKE_OP(coerce_a, 0);
MAKE_OP(coerce_s, 0);
MAKE_OP(construct, 1, OpcodeArgType::A_U30);
MAKE_OP(constructprop, 2, OpcodeArgType::A_Multiname, OpcodeArgType::A_U30);
MAKE_OP(constructsuper, 1, OpcodeArgType::A_U30);
MAKE_OP(convert_b, 0);
MAKE_OP(convert_i, 0);
MAKE_OP(convert_d, 0);
MAKE_OP(convert_o, 0);
MAKE_OP(convert_u, 0);
MAKE_OP(convert_s, 0);
MAKE_OP(debug, 4, OpcodeArgType::A_UByte, OpcodeArgType::A_String, OpcodeArgType::A_UByte, OpcodeArgType::A_U30);
MAKE_OP(debugfile, 1, OpcodeArgType::A_String);
MAKE_OP(debugline, 1, OpcodeArgType::A_U30);
MAKE_OP(declocal, 1, OpcodeArgType::A_U30);
MAKE_OP(declocal_i, 1, OpcodeArgType::A_U30);
MAKE_OP(decrement, 0);
MAKE_OP(decrement_i, 0);
MAKE_OP(deleteproperty, 1, OpcodeArgType::A_Multiname);
MAKE_OP(divide, 0);
MAKE_OP(dup, 0);
MAKE_OP(dxns, 1, OpcodeArgType::A_String);
MAKE_OP(dxnslate, 0);
MAKE_OP(equals, 0);
MAKE_OP(esc_xattr, 0);
MAKE_OP(esc_xelem, 0);
MAKE_OP(findproperty, 1, OpcodeArgType::A_Multiname);
MAKE_OP(findpropstrict, 1, OpcodeArgType::A_Multiname);
MAKE_OP(getdescendants, 1, OpcodeArgType::A_Multiname);
MAKE_OP(getglobalscope, 0);
MAKE_OP(getglobalslot, 1, OpcodeArgType::A_U30);//
MAKE_OP(getlex, 1, OpcodeArgType::A_Multiname);
MAKE_OP(getlocal, 1, OpcodeArgType::A_U30);
MAKE_OP(getlocal_0, 0);
MAKE_OP(getlocal_1, 0);
MAKE_OP(getlocal_2, 0);
MAKE_OP(getlocal_3, 0);
MAKE_OP(getproperty, 1, OpcodeArgType::A_Multiname);
MAKE_OP(getscopeobject, 1, OpcodeArgType::A_UByte);
MAKE_OP(getslot, 1, OpcodeArgType::A_U30);//
MAKE_OP(getsuper, 1, OpcodeArgType::A_Multiname);
MAKE_OP(greaterequals, 0);
MAKE_OP(greaterthan, 0);
MAKE_OP(hasnext, 0);
MAKE_OP(hasnext2, 2, OpcodeArgType::A_U30, OpcodeArgType::A_U30);
MAKE_OP(ifeq, 1, OpcodeArgType::A_S24);
MAKE_OP(iffalse, 1, OpcodeArgType::A_S24);
MAKE_OP(ifge, 1, OpcodeArgType::A_S24);
MAKE_OP(ifgt, 1, OpcodeArgType::A_S24);
MAKE_OP(ifle, 1, OpcodeArgType::A_S24);
MAKE_OP(iflt, 1, OpcodeArgType::A_S24);
MAKE_OP(ifnge, 1, OpcodeArgType::A_S24);
MAKE_OP(ifngt, 1, OpcodeArgType::A_S24);
MAKE_OP(ifnle, 1, OpcodeArgType::A_S24);
MAKE_OP(ifnlt, 1, OpcodeArgType::A_S24);
MAKE_OP(ifne, 1, OpcodeArgType::A_S24);
MAKE_OP(ifstricteq, 1, OpcodeArgType::A_S24);
MAKE_OP(ifstrictne, 1, OpcodeArgType::A_S24);
MAKE_OP(iftrue, 1, OpcodeArgType::A_S24);
MAKE_OP(in, 0);
MAKE_OP(inclocal, 1, OpcodeArgType::A_U30);
MAKE_OP(inclocal_i, 1, OpcodeArgType::A_U30);
MAKE_OP(increment, 0);
MAKE_OP(increment_i, 0);
MAKE_OP(initproperty, 1, OpcodeArgType::A_Multiname);
MAKE_OP(instanceof, 0);
MAKE_OP(istype, 1, OpcodeArgType::A_Multiname);
MAKE_OP(istypelate, 0);
MAKE_OP(jump, 1, OpcodeArgType::A_S24);
MAKE_OP(kill, 1, OpcodeArgType::A_U30);
MAKE_OP(label, 0);
MAKE_OP(lessequals, 0);
MAKE_OP(lessthan, 0);
MAKE_OP(lookupswitch, 2, OpcodeArgType::A_S24, OpcodeArgType::A_U30);
MAKE_OP(lshift, 0);
MAKE_OP(modulo, 0);
MAKE_OP(multiply, 0);
MAKE_OP(multiply_i, 0);
MAKE_OP(negate, 0);
MAKE_OP(negate_i, 0);
MAKE_OP(newactivation, 0);
MAKE_OP(newarray, 1, OpcodeArgType::A_U30);
MAKE_OP(newcatch, 1, OpcodeArgType::A_U30);//
MAKE_OP(newclass, 1, OpcodeArgType::A_U30);//
MAKE_OP(newfunction, 1, OpcodeArgType::A_U30);//
MAKE_OP(newobject, 1, OpcodeArgType::A_U30);
MAKE_OP(nextname, 0);
MAKE_OP(nextvalue, 0);
MAKE_OP(nop, 0);
MAKE_OP(not, 0);
MAKE_OP(pop, 0);
MAKE_OP(popscope, 0);
MAKE_OP(pushbyte, 1, OpcodeArgType::A_UByte);
MAKE_OP(pushdouble, 1, OpcodeArgType::A_double);
MAKE_OP(pushfalse, 0);
MAKE_OP(pushint, 1, OpcodeArgType::A_int);
MAKE_OP(pushnamespace, 1, OpcodeArgType::A_Namespace);
MAKE_OP(pushnan, 0);
MAKE_OP(pushnull, 0);
MAKE_OP(pushscope, 0);
MAKE_OP(pushshort, 1, OpcodeArgType::A_U30);
MAKE_OP(pushstring, 1, OpcodeArgType::A_String);
MAKE_OP(pushtrue, 0);
MAKE_OP(pushuint, 1, OpcodeArgType::A_uint);
MAKE_OP(pushundefined, 0);
MAKE_OP(pushwith, 0);
MAKE_OP(returnvalue, 0);
MAKE_OP(returnvoid, 0);
MAKE_OP(rshift, 0);
MAKE_OP(setlocal, 1, OpcodeArgType::A_U30);
MAKE_OP(setlocal_0, 0);
MAKE_OP(setlocal_1, 0);
MAKE_OP(setlocal_2, 0);
MAKE_OP(setlocal_3, 0);
MAKE_OP(setglobalslot, 1, OpcodeArgType::A_U30);//
MAKE_OP(setproperty, 1, OpcodeArgType::A_Multiname);
MAKE_OP(setslot, 1, OpcodeArgType::A_U30);//
MAKE_OP(setsuper, 1, OpcodeArgType::A_Multiname);
MAKE_OP(strictequals, 0);
MAKE_OP(subtract, 0);
MAKE_OP(subtract_i, 0);
MAKE_OP(swap, 0);
MAKE_OP(throw, 0);
MAKE_OP(typeof, 0);
MAKE_OP(urshift, 0);

OpcodeInstruction::OpcodeInstruction( OpcodeOperation* operation, Opcode code, char* data, size_t dataLen )
	: operation(operation)
	, code(code)
	, data(data)
	, dataLen(dataLen)
{

}

OpcodeInstruction::~OpcodeInstruction()
{
	if (data)
		free(data);
}

void OpcodeAnalyzer::analyze( CpoolInfo* cpool, SWFInputStream* input, std::vector<OpcodeInstruction*>& instructions )
{
	while (input->getRemain() > 0)
	{
		size_t dataPos = input->getPosition();
		Opcode code = (Opcode) input->readU8();
		OpcodeOperation* operation = OpcodeOperation::getOperation(code);
		std::string paramStr;
		for (OpcodeArgType type : operation->arguments)
		{
			switch (type)
			{
			case OpcodeArgType::A_String:
				{
					auto s = cpool->getString(input->readU30());
					paramStr += s + ",";
				}
				break;
			case OpcodeArgType::A_Multiname:
				{
					auto mn = cpool->getMultinameInfo(input->readU30());
					paramStr += mn->toString() + ",";
				}
				break;
			case OpcodeArgType::A_int:
				{
					char buffer[10] = {0};
					UI30 v = input->readU30();
					sprintf(buffer, "%d", v);
					paramStr += buffer;
					paramStr += ",";
				}
				break;
			case OpcodeArgType::A_S24:
				{
					char buffer[10] = {0};
					SI24 v = input->readS24();
					sprintf(buffer, "%d", v);
					paramStr += buffer;
					paramStr += ",";
				}
				break;
			case OpcodeArgType::A_UByte:
				{
					char buffer[10] = {0};
					UI8 v = input->readU8();
					sprintf(buffer, "%d", v);
					paramStr += buffer;
					paramStr += ",";
				}
				break;
			default:
				input->readU30();
				break;
			}
		}

		char* data = nullptr;
		size_t dataLen = input->getPosition() - dataPos;
		if (dataLen > 0)
		{
			data = (char*) malloc(dataLen);
			input->setPosition(dataPos);
			input->readBytes(data, dataLen);
		}
		OpcodeInstruction* instruction = new OpcodeInstruction(operation, code, data, dataLen);
		instruction->paramString = paramStr;
		instructions.push_back(instruction);
	}
}
